home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / OWLSRC.PAK / PANESPLI.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  42.2 KB  |  1,688 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1995, 1997 by Borland International, All Rights Reserved
  4. //
  5. //$Revision:   10.21  $
  6. //
  7. // Implementation of Pane Splitter classes
  8. //----------------------------------------------------------------------------
  9. #include <owl/pch.h>
  10. #if !defined(OWL_LAYOUTWI_H)
  11. # include <owl/layoutwi.h>
  12. #endif
  13. #if !defined(OWL_PANESPLI_H)
  14. # include <owl/panespli.h>
  15. #endif
  16. #if !defined(OWL_UIHELPER_H)
  17. # include <owl/uihelper.h>
  18. #endif
  19. #if !defined(OWL_DC_H)
  20. # include <owl/dc.h>
  21. #endif
  22. #if !defined(WINSYS_UIMETRIC_H)
  23. # include <winsys/uimetric.h>
  24. #endif
  25. #if !defined(CLASSLIB_STACKS_H)
  26. # include <classlib/stacks.h>
  27. #endif
  28. #include <math.h>
  29.  
  30. OWL_DIAGINFO;
  31.  
  32. //
  33. // Defines to make typesafe downcasting simpler.
  34. //
  35. #define SPLITTER(x) TYPESAFE_DOWNCAST(x,TSplitter)
  36. #define LAYOUTWINDOW(x) TYPESAFE_DOWNCAST(x,TLayoutWindow)
  37. //#define USE_CUSTOM_CURSORS
  38.  
  39. //----------------------------------------------------------------------------
  40. // TSplitter class.
  41. //
  42.  
  43. DEFINE_RESPONSE_TABLE1(TSplitter, TLayoutWindow)
  44.   EV_WM_LBUTTONDOWN,
  45.   EV_WM_MOUSEMOVE,
  46.   EV_WM_LBUTTONUP,
  47.   EV_WM_SETCURSOR,
  48.   EV_WM_SIZE,
  49. END_RESPONSE_TABLE;
  50.  
  51. //
  52. // Constructor for abstract base class - don't set a border.
  53. //
  54. TSplitter::TSplitter(TWindow* parent, TPaneSplitter* ps, float percent)
  55. :
  56.   TLayoutWindow(parent, 0, 0),
  57.   PercentOf(percent),
  58.   PaneSplitter(ps)
  59. {
  60.   Attr.Style &= ~WS_BORDER;
  61.   Attr.Style |= WS_CLIPCHILDREN;
  62. }
  63.  
  64. //
  65. // Notify TPaneSplitter object that it should begin the splitter move process.
  66. //
  67. void
  68. TSplitter::EvLButtonDown(uint /*modKeys*/, TPoint& point)
  69. {
  70.   TPoint  p = point;
  71.   MapWindowPoints(0, &p, 1);  // map to screen
  72.   PaneSplitter->StartSplitterMove(this, p);
  73. //  TLayoutWindow::EvLButtonDown(modKeys, point);
  74. }
  75.  
  76. //
  77. // Notify TPaneSplitter object that splitter has moved.
  78. //
  79. void
  80. TSplitter::EvMouseMove(uint /*modKeys*/, TPoint& point)
  81. {
  82.   TPoint  p = point;
  83.   MapWindowPoints(0, &p, 1);  // map to screen
  84.   PaneSplitter->MouseMoved(p);
  85. //  TLayoutWindow::EvMouseMove(modKeys, point);
  86. }
  87.  
  88. //
  89. // Notify TPaneSplitter object that splitters have finished being moved.
  90. //
  91. void
  92. TSplitter::EvLButtonUp(uint /*modKeys*/, TPoint& /*point*/)
  93. {
  94.   PaneSplitter->EndSplitterMove();
  95. //  TLayoutWindow::EvLButtonUp(modKeys, point);  
  96. }
  97.  
  98. //
  99. // Notify TPaneSplitter object that it should set the appropriate cursor.
  100. //
  101. bool
  102. TSplitter::EvSetCursor(THandle hWndCursor, uint hitTest, uint mouseMsg)
  103. {
  104.   TPoint p;
  105.   if (hWndCursor == GetHandle() && hitTest == HTCLIENT) {
  106.     GetCursorPos(p);
  107.     PaneSplitter->SetSplitterMoveCursor(this, p);
  108.   }
  109.   else
  110.     TLayoutWindow::EvSetCursor(hWndCursor, hitTest, mouseMsg);
  111.   return true;
  112. }
  113.  
  114. //
  115. // Notify TPaneSplitter object that splitter needs to be repainted.
  116. // This allows the user to draw the splitter.
  117. //
  118. void
  119. TSplitter::Paint(TDC& dc, bool, TRect& rect)
  120. {
  121.   TLayoutWindow::Paint(dc, true, rect);
  122.   PaneSplitter->DrawSplitter(dc, GetRect());
  123. }
  124.  
  125. //
  126. // Resize the splitter.  In certain situations the splitters that are
  127. // nested in this splitter don't want to resize, in which case their
  128. // sizes are adjusted (retained).
  129. //
  130. void
  131. TSplitter::EvSize(uint sizeType, TSize& size)
  132. {
  133.   if (sizeType != SIZE_MINIMIZED && PaneSplitter->PaneSplitterResizing)
  134.     AdjForResize(size);
  135.   TLayoutWindow::EvSize(sizeType, size);
  136. }
  137.  
  138. //
  139. // Common code for T*Splitter::Setup().  Sets pane's parent to this and
  140. // sets their layout metrics.
  141. //
  142. void
  143. TSplitter::SetupEpilog(TSplitter* s, TWindow* targetPane, TWindow* newPane,
  144.                        TLayoutMetrics& lmOfTargetPane,
  145.                        TLayoutMetrics& lmOfNewPane)
  146. {
  147.   targetPane->SetParent(s);
  148.   newPane->SetParent(s);
  149.   newPane->Create();
  150.   SetChildLayoutMetrics(*targetPane, lmOfTargetPane);
  151.   SetChildLayoutMetrics(*newPane, lmOfNewPane);
  152. }
  153.  
  154. //
  155. // Remove a pane from this splitter.
  156. //
  157. TLayoutWindow*
  158. TSplitter::RemovePane(TWindow* pane, TShouldDelete::TDelete dt)
  159. {
  160.   // 'parentSplitter' represents the splitter 'this' splitter is contained in.
  161.   // 'retval' indicates weather their is a parent splitter, if not then 'pane'
  162.   // is only one.
  163.   // 'otherPane' is other window contained in this splitter.
  164.   //
  165.   TSplitter*        parentSplitter = SPLITTER(Parent);
  166.   TLayoutWindow*    retval = parentSplitter;
  167.   TWindow*          otherPane = Pane1() == pane ? Pane2() : Pane1();
  168.  
  169.   TLayoutMetrics    lm1;
  170.   TLayoutMetrics    lm2;
  171.   TLayoutMetrics    lm3;
  172.  
  173.   pane->ShowWindow(SW_HIDE);
  174.   if (parentSplitter) {
  175.     TWindow*          p1 = parentSplitter->Pane1();
  176.     TWindow*          p2 = parentSplitter->Pane2();
  177.  
  178.     parentSplitter->GetChildLayoutMetrics(*p1, lm1);
  179.     parentSplitter->GetChildLayoutMetrics(*p2, lm2);
  180.     pane->SetParent(0);               // remove pane.
  181.     otherPane->SetParent(parentSplitter);   // move up one level.
  182.  
  183.     if (this == p1) {                 // if top or left pane is being removed.
  184.       if (lm2.X.RelWin == this)       // adjust other panes layout metrics.
  185.         lm2.X.RelWin = otherPane;
  186.       else if (lm2.Y.RelWin == this)
  187.         lm2.Y.RelWin = otherPane;
  188.       parentSplitter->SetChildLayoutMetrics(*p2, lm2);
  189.       parentSplitter->SetChildLayoutMetrics(*otherPane, lm1);
  190.     }
  191.     else {  // other pane's layout is same as one being removed.
  192.             //
  193.       parentSplitter->SetChildLayoutMetrics(*otherPane, lm2);
  194.     }
  195.   }
  196.   else {                          // remove last pane.
  197.     retval = LAYOUTWINDOW(Parent);
  198.     TLayoutMetrics  lmOfOtherPane;
  199.  
  200.     PaneSplitter->GetDefLM(lmOfOtherPane);
  201.     otherPane->SetParent(retval);
  202.     pane->SetParent(0);
  203.     retval->SetChildLayoutMetrics(*otherPane, lmOfOtherPane);
  204.   }
  205.   // Destroy pane.
  206.   //
  207.   RemoveChildLayoutMetrics(*pane);
  208.   PaneSplitter->DestroyPane(pane, dt);
  209.   return retval;
  210. }
  211.  
  212. //----------------------------------------------------------------------------
  213. // TVSplitter class.
  214. //
  215.  
  216. //
  217. // Constructor for a vertical splitter
  218. //
  219. TVSplitter::TVSplitter(TWindow* parent, TPaneSplitter* ps, float percent)
  220. :
  221.   TSplitter(parent, ps, percent)
  222. {
  223.   SetCaption("VSplitter");
  224. }
  225.  
  226. //
  227. // Return TRect for the visible area (in client coordinates) of splitter.
  228. //
  229. TRect
  230. TVSplitter::GetRect()
  231. {
  232.   TRect     rect;
  233.   TWindow*  p1 = Pane1();
  234.  
  235.   rect.left = p1->Attr.X + p1->Attr.W;
  236.   rect.top = p1->Attr.Y;
  237.   rect.right = rect.left + PaneSplitter->GetSplitterWidth();
  238.   rect.bottom = p1->Attr.Y + p1->Attr.H;
  239.  
  240.   return rect;
  241. }
  242.  
  243. //
  244. // Split given pane into 2 parts: itself and a new pane.
  245. //
  246. void
  247. TVSplitter::Split(TWindow* targetPane, TWindow* newPane, TSplitDirection sd, float percent)
  248. {
  249.   // Panes contained in this splitter.
  250.   //
  251.   TWindow*        p1 = Pane1();
  252.   TWindow*        p2 = Pane2();
  253.   TLayoutMetrics  lm;
  254.  
  255.   GetChildLayoutMetrics(*p2, lm);
  256.  
  257.   // Create new splitter (will be a child of this splitter).
  258.   //
  259.   TSplitter* splitter;
  260.   if (sd == psHorizontal)
  261.     splitter = new THSplitter(this, PaneSplitter, percent);
  262.   else
  263.     splitter = new TVSplitter(this, PaneSplitter, percent);
  264.  
  265.   // Initialize new splitter by putting target and new pane into it.
  266.   //
  267.   splitter->Create();
  268.   TLayoutMetrics lmOfSplitter = splitter->Setup(targetPane, newPane, percent);
  269.  
  270.   if (p1 == targetPane) {   // target pane == left pane.  adjust new splitter's
  271.                             // width and other windows left edge to be
  272.                             // adjacent to new splitter.
  273.                             //
  274.     lmOfSplitter.Width.Absolute(targetPane->Attr.W);
  275.  
  276.     lm.X.RelWin = splitter;
  277.     SetChildLayoutMetrics(*p2, lm);
  278.   }
  279.   else {                    // set new splitter to the right of left pane.
  280.                             //
  281.     lmOfSplitter.X.RightOf(p1, PaneSplitter->GetSplitterWidth());
  282.   }
  283.   SetChildLayoutMetrics(*splitter, lmOfSplitter);
  284. }
  285.  
  286. //
  287. // Setup layout metrics and children for new splitter. Return that
  288. // layout metrics.
  289. //
  290. TLayoutMetrics
  291. TVSplitter::Setup(TWindow* targetPane, TWindow* newPane, float percent)
  292. {
  293.   TLayoutMetrics  lmOfSplitter;
  294.   TLayoutMetrics  lmOfPane1;
  295.   TLayoutMetrics  lmOfPane2;
  296.  
  297.   PaneSplitter->GetDefLM(lmOfSplitter);
  298.   lmOfPane1 = lmOfPane2 = lmOfSplitter;
  299.  
  300.   // Target pane's width is 1/2 of what it was.  New pane is to the right
  301.   // of target pane.
  302.   //
  303.   lmOfPane1.Width.Absolute(targetPane->Attr.W * percent);
  304.   lmOfPane2.X.RightOf(targetPane, PaneSplitter->GetSplitterWidth());
  305.   SetupEpilog(this, targetPane, newPane, lmOfPane1, lmOfPane2);
  306.  
  307.   return lmOfSplitter;
  308. }
  309.  
  310. //
  311. // Create splitter indicator for this splitter.
  312. //
  313. TSplitterIndicator*
  314. TVSplitter::CreateSplitterIndicator()
  315. {
  316.   return new TVSplitterIndicator(this, GetScreenRect());
  317. }
  318.  
  319. //
  320. // Return left pane, or 0 if none exist.
  321. //
  322. TWindow*
  323. TVSplitter::Pane1()
  324. {
  325.   TWindow*  p1 = GetFirstChild();
  326.   if (p1) {
  327.     TWindow*  p2 = p1->Next();
  328.  
  329.     return p1->Attr.X <= p2->Attr.X ? p1 : p2;
  330.   }
  331.   return 0;
  332. }
  333.  
  334. //
  335. // Return right pane, or 0 if none exist.
  336. //
  337. TWindow*
  338. TVSplitter::Pane2()
  339. {
  340.   if (NumChildren() != 2)
  341.     return 0;
  342.  
  343.   TWindow*  p1 = GetFirstChild();
  344.   TWindow*  p2 = p1->Next();
  345.  
  346.   return p1->Attr.X > p2->Attr.X ? p1 : p2;
  347. }
  348.  
  349. //
  350. // Called as a result of parent being resized. Adjust width of pane 1 to make
  351. // it appear this splitter didn't move.
  352. //
  353. void
  354. TVSplitter::AdjForResize(const TSize& sz)
  355. {
  356.   TLayoutMetrics     lm;
  357.   TWindow*           pane = Pane1();
  358.  
  359.   if (pane) {
  360.     GetChildLayoutMetrics(*pane, lm);
  361.     lm.Width.Absolute(GetPercent() * sz.cx);
  362.     SetChildLayoutMetrics(*pane, lm);
  363.   }
  364. }
  365.  
  366. //
  367. // Move splitter given distance.  Accomplished by resizing pane 1.
  368. //
  369. void
  370. TVSplitter::Move(int dist)
  371. {
  372.   TLayoutMetrics  lm;
  373.   TWindow*        pane = Pane1();
  374.  
  375.   if (pane) {
  376.     GetChildLayoutMetrics(*pane, lm);
  377.     lm.Width.Absolute(lm.Width.Value + dist);
  378.     SetChildLayoutMetrics(*pane, lm);
  379.  
  380.     // Calc new percent.
  381.     //
  382.     PercentOf = (float)lm.Width.Value / (float)Attr.W;
  383.  
  384.     // Update nested splitters to retain their positions.
  385.     //
  386.     TSplitter* splitter = SPLITTER(Pane2());
  387.     if (splitter) {
  388.       while (1) {
  389.         if (splitter->SplitDirection() == SplitDirection()) {
  390.           TLayoutMetrics lm;
  391.           splitter->GetChildLayoutMetrics(*splitter->Pane1(), lm);
  392.           lm.Width.Absolute(lm.Width.Value - dist);
  393.           splitter->SetPercent((float)lm.Width.Value /
  394.                                (float)(splitter->Attr.W - dist));
  395.           splitter->SetChildLayoutMetrics(*splitter->Pane1(), lm);
  396.         }
  397.         if ((splitter = SPLITTER(splitter->Pane1())) == 0)
  398.           break;
  399.       }
  400.     }
  401.   }
  402. }
  403.  
  404. //
  405. // Change the width of the splitter and then do a Layout() to see changes.
  406. //
  407. void
  408. TVSplitter::AdjSplitterWidth(int w)
  409. {
  410.   TLayoutMetrics  lm;
  411.  
  412.   GetChildLayoutMetrics(*Pane2(), lm);
  413.   lm.X.Value += w;
  414.   SetChildLayoutMetrics(*Pane2(), lm);
  415.   Layout();
  416. }
  417.  
  418. //----------------------------------------------------------------------------
  419. // THSplitter class.
  420. //
  421.  
  422. //
  423. // Constructor for a horizontal splitter
  424. //
  425. THSplitter::THSplitter(TWindow* parent, TPaneSplitter* ps, float percent)
  426. :
  427.   TSplitter(parent, ps, percent)
  428. {
  429.   SetCaption("HSplitter");
  430. }
  431.  
  432. //
  433. // Return TRect for the visible area (in client coordinates) of splitter.
  434. //
  435. TRect
  436. THSplitter::GetRect()
  437. {
  438.   TRect     rect;
  439.   TWindow*  p1 = Pane1();
  440.  
  441.   rect.left = p1->Attr.X;
  442.   rect.top = p1->Attr.Y + p1->Attr.H;
  443.   rect.right = p1->Attr.X + p1->Attr.W;
  444.   rect.bottom = rect.top + PaneSplitter->GetSplitterWidth();
  445.  
  446.   return rect;
  447. }
  448.  
  449. //
  450. // Split given pane into 2 parts: itself and a new pane.
  451. //
  452. void
  453. THSplitter::Split(TWindow* targetPane, TWindow* newPane, TSplitDirection sd, float percent)
  454. {
  455.   // Panes contained in this splitter.
  456.   //
  457.   TWindow*       p1 = Pane1();
  458.   TWindow*       p2 = Pane2();
  459.   TLayoutMetrics lm;
  460.  
  461.   GetChildLayoutMetrics(*p2, lm);
  462.  
  463.   // Create new splitter (will be a child of this splitter).
  464.   //
  465.   TSplitter* splitter;
  466.   if (sd == psHorizontal)
  467.     splitter = new THSplitter(this, PaneSplitter, percent);
  468.   else
  469.     splitter = new TVSplitter(this, PaneSplitter, percent);
  470.  
  471.   // Initialize new splitter by putting target and new pane into it.
  472.   //
  473.   splitter->Create();
  474.   TLayoutMetrics lmOfSplitter = splitter->Setup(targetPane, newPane, percent);
  475.  
  476.   if (p1 == targetPane) {   // target pane == top pane.  adjust new splitter's
  477.                             // height and other windows top edge to be
  478.                             // adjacent to new splitter.
  479.                             //
  480.     lmOfSplitter.Height.Absolute(targetPane->Attr.H);
  481.  
  482.     lm.Y.RelWin = splitter;
  483.     SetChildLayoutMetrics(*p2, lm);
  484.   }
  485.   else {                    // set new splitter to below top pane.
  486.                             //
  487.     lmOfSplitter.Y.Below(p1, PaneSplitter->GetSplitterWidth());
  488.   }
  489.   SetChildLayoutMetrics(*splitter, lmOfSplitter);
  490. }
  491.  
  492. //
  493. // Setup layout metrics and children for new splitter. Return that
  494. // layout metrics.
  495. //
  496. TLayoutMetrics
  497. THSplitter::Setup(TWindow* targetPane, TWindow* newPane, float percent)
  498. {
  499.   TLayoutMetrics  lmOfSplitter;
  500.   TLayoutMetrics  lmOfPane1;
  501.   TLayoutMetrics  lmOfPane2;
  502.  
  503.   PaneSplitter->GetDefLM(lmOfSplitter);
  504.   lmOfPane1 = lmOfPane2 = lmOfSplitter;
  505.  
  506.   // Target pane's height is 1/2 of what it was.  New pane is below
  507.   // target pane.
  508.   //
  509.   lmOfPane1.Height.Absolute(targetPane->Attr.H * percent);
  510.   lmOfPane2.Y.Below(targetPane, PaneSplitter->GetSplitterWidth());
  511.   SetupEpilog(this, targetPane, newPane, lmOfPane1, lmOfPane2);
  512.  
  513.   return lmOfSplitter;
  514. }
  515.  
  516. //
  517. // Create splitter indicator for this splitter.
  518. //
  519. TSplitterIndicator*
  520. THSplitter::CreateSplitterIndicator()
  521. {
  522.   return new THSplitterIndicator(this, GetScreenRect());
  523. }
  524.  
  525. //
  526. // Return top pane, or 0 if none exist.
  527. //
  528. TWindow*
  529. THSplitter::Pane1()
  530. {
  531.   TWindow* p1 = GetFirstChild();
  532.  
  533.   if (p1) {
  534.     TWindow*  p2 = p1->Next();
  535.  
  536.     return p1->Attr.Y <= p2->Attr.Y ? p1 : p2;
  537.   }
  538.   return 0;
  539. }
  540.  
  541. //
  542. // Return bottom pane, or 0 if none exist.
  543. //
  544. TWindow*
  545. THSplitter::Pane2()
  546. {
  547.   if (NumChildren() != 2)
  548.     return 0;
  549.  
  550.   TWindow* p1 = GetFirstChild();
  551.   TWindow* p2 = p1->Next();
  552.  
  553.   return p1->Attr.Y > p2->Attr.Y ? p1 : p2;
  554. }
  555.  
  556. //
  557. // Called as a result of parent being resized. Adjust height of pane 1 to make
  558. // it appear this splitter didn't move.
  559. //
  560. void
  561. THSplitter::AdjForResize(const TSize& sz)
  562. {
  563.   TLayoutMetrics lm;
  564.   TWindow*       pane = Pane1();
  565.  
  566.   if (pane) {
  567.     GetChildLayoutMetrics(*pane, lm);
  568.     lm.Height.Absolute(GetPercent() * sz.cy);
  569.     SetChildLayoutMetrics(*pane, lm);
  570.   }
  571. }
  572.  
  573. //
  574. // Move splitter given distance.  Accomplished by resizing pane 1.
  575. //
  576. void
  577. THSplitter::Move(int dist)
  578. {
  579.   TLayoutMetrics  lm;
  580.   TWindow*        pane = Pane1();
  581.  
  582.   if (pane) {
  583.     GetChildLayoutMetrics(*pane, lm);
  584.     lm.Height.Absolute(lm.Height.Value + dist);
  585.     SetChildLayoutMetrics(*pane, lm);
  586.  
  587.     // Calc new percent.
  588.     //
  589.     PercentOf = (float)lm.Height.Value / (float)Attr.H;
  590.  
  591.     // Update nested splitters to retain there relative positions.
  592.     //
  593.     TSplitter* splitter = SPLITTER(Pane2());
  594.     if (splitter) {
  595.       while (1) {
  596.         if (splitter->SplitDirection() == SplitDirection()) {
  597.           TLayoutMetrics lm;
  598.           splitter->GetChildLayoutMetrics(*splitter->Pane1(), lm);
  599.           lm.Height.Absolute(lm.Height.Value - dist);
  600.           splitter->SetPercent((float)lm.Height.Value /
  601.                                (float)(splitter->Attr.H - dist));
  602.           splitter->SetChildLayoutMetrics(*splitter->Pane1(), lm);
  603.         }
  604.         if ((splitter = SPLITTER(splitter->Pane1())) == 0)
  605.           break;
  606.       }
  607.     }
  608.   }
  609. }
  610.  
  611. //
  612. // Change the width of the splitter and then do a Layout() to see changes.
  613. //
  614. void
  615. THSplitter::AdjSplitterWidth(int w)
  616. {
  617.   TLayoutMetrics  lm;
  618.  
  619.   GetChildLayoutMetrics(*Pane2(), lm);
  620.   lm.Y.Value += w;
  621.   SetChildLayoutMetrics(*Pane2(), lm);
  622.   Layout();
  623. }
  624.  
  625. //----------------------------------------------------------------------------
  626. // TSplitterIndicator class (Base class).
  627. //
  628.  
  629. //
  630. // Draw splitter indicator.  Use InvertRect() to create the effect.
  631. //
  632. void
  633. TSplitterIndicator::Draw()
  634. {
  635.   if (!Showing) {
  636.     Showing = true;
  637.     TScreenDC screenDC;
  638.     screenDC.InvertRect(*this);
  639.   }
  640. }
  641.  
  642. //----------------------------------------------------------------------------
  643. // TVSplitterIndicator class.
  644. //
  645.  
  646. //
  647. // Connect either top or bottom side of indicator to either top or bottom side
  648. // of given TRect. If top of given TRect is below indicator then connect to
  649. // TRect's top, else if TRect is above indicator then connect to TRect's bottom.
  650. // Assumes that given TRect represents a horizontal indicator.
  651. //
  652. void
  653. TVSplitterIndicator::ConnectToRect(const TRect& rect)
  654. {
  655.   if (rect.top > bottom)
  656.     bottom = rect.top;
  657.   else if (rect.top < top)
  658.     top = rect.bottom;
  659. }
  660.  
  661. //
  662. // Move splitter indicator (do not draw it though) given dist, which
  663. // could be + (move right) or - (move left).  If the move would put the
  664. // indicator out of the move area then move to edge of move area.
  665. //
  666. void
  667. TVSplitterIndicator::Move(int dist)
  668. {
  669.   TRect moveArea = Splitter->GetMoveArea();
  670.   TRect orig = Splitter->GetScreenRect();
  671.   int   splitterWidth = right - left;
  672.  
  673.   left = orig.left + dist;
  674.   right = left + splitterWidth;
  675.   top = orig.top;
  676.   bottom = orig.bottom;
  677.  
  678.   //
  679.   // Don't allow the indicator to get closer to the edge of the moveArea
  680.   // than the cushion.
  681.   //
  682.   if( moveArea.Width() > (Cushion) )  {
  683.     moveArea.left += Cushion;
  684.     moveArea.right -= Cushion;
  685.   }
  686.   else {
  687.     DistMoved = 0;
  688.     return;
  689.   }
  690.  
  691.  
  692.   if (!moveArea.Contains(*this)) {
  693.     // Indicator out of move area to put it adjacent to either top or bottom
  694.     // of move area.
  695.     //
  696.     if (left < moveArea.left) {
  697.       left = moveArea.left;
  698.       right = left + splitterWidth;
  699.     }
  700.     else {
  701.       right = moveArea.right;
  702.       left = right - splitterWidth;
  703.     }
  704.   }
  705.   else
  706.     DistMoved = dist;
  707. }
  708.  
  709. //
  710. // Return distance moved (left or right) given starting and current point.
  711. //
  712. int
  713. TVSplitterIndicator::CalcDistMoved(const TPoint& start, const TPoint& cur)
  714. {
  715.   return cur.x - start.x;
  716. }
  717.  
  718. //
  719. // A check to see if given point could be in indicator if indicator were
  720. // streched.  This function is used when determining if multiple splitters
  721. // intersect at a point (for dragging).
  722. //
  723. bool
  724. TVSplitterIndicator::CouldContain(const TPoint& point)
  725. {
  726.   TRect r = *this;
  727.   int   splitterWidth = right - left;
  728.  
  729.   r.top -= splitterWidth;
  730.   r.bottom += splitterWidth;
  731.   return r.Contains(point);
  732. }
  733.  
  734. //
  735. // Calculate the area in which the indicator moved.  Use original splitter
  736. // position and current indicator position to calculate area. Used to
  737. // determine which panes need to be removed.
  738. //
  739. TRect
  740. TVSplitterIndicator::CalcAreaOfSplitterMove()
  741. {
  742.   TRect r1 = *this;
  743.   TRect r2 = Splitter->GetScreenRect();
  744.   TRect area = r2;
  745.  
  746.   if (r1.left < r2.left) {
  747.     area.left = r1.left;
  748.     area.right = r2.right;
  749.   }
  750.   else {
  751.     area.left = r2.left;
  752.     area.right = r1.right;
  753.   }
  754.   return area;
  755. }
  756.  
  757. //----------------------------------------------------------------------------
  758. // THSplitterIndicator class.
  759. //
  760.  
  761. //
  762. // Connect either left or right side of indicator to either left or right side
  763. // of given TRect. If left side of given TRect is to the right of indicator
  764. // then connect to TRect's left side, else if TRect is to the left of indicator
  765. // then connect to TRect's right side. Assumes that given TRect represents a
  766. // vertical indicator.
  767. //
  768. void
  769. THSplitterIndicator::ConnectToRect(const TRect& rect)
  770. {
  771.   if (rect.left > right)
  772.     right = rect.left;
  773.   else if (rect.left < left)
  774.     left = rect.right;
  775. }
  776.  
  777. //
  778. // Move splitter indicator (do not draw it though) given dist, which
  779. // could be + (move down) or - (move up).  If the move would put the
  780. // indicator out of the move area then move to edge of move area.
  781. //
  782. void
  783. THSplitterIndicator::Move(int dist)
  784. {
  785.   TRect moveArea = Splitter->GetMoveArea();
  786.   TRect orig = Splitter->GetScreenRect();
  787.   int   splitterWidth = bottom - top;
  788.  
  789.   top = orig.top + dist;
  790.   bottom = top + splitterWidth;
  791.   left = orig.left;
  792.   right = orig.right;
  793.  
  794.   //
  795.   // Don't allow the indicator to get closer to the edge of the moveArea
  796.   // than the cushion.
  797.   //
  798.   if( moveArea.Height() > (Cushion) )  {
  799.     moveArea.top += Cushion;
  800.     moveArea.bottom -= Cushion;
  801.   }
  802.   else {
  803.     DistMoved = 0;
  804.     return;
  805.   }
  806.  
  807.   if (!moveArea.Contains(*this)) {
  808.     // Indicator out of move area to put it adjacent to either top or bottom
  809.     // of move area.
  810.     //
  811.     if (top < moveArea.top) {
  812.       top = moveArea.top;
  813.       bottom = top + splitterWidth;
  814.     }
  815.     else {
  816.       bottom = moveArea.bottom;
  817.       top = bottom - splitterWidth;
  818.     }
  819.   }
  820.   else
  821.     DistMoved = dist;
  822. }
  823.  
  824. //
  825. // Return distance moved (up or down) given starting and current point.
  826. //
  827. int
  828. THSplitterIndicator::CalcDistMoved(const TPoint& start, const TPoint& cur)
  829. {
  830.   return cur.y - start.y;
  831. }
  832.  
  833. //
  834. // A check to see if given point could be in indicator if indicator were
  835. // streched.  This function is used when determining if multiple splitters
  836. // intersect at a point (for dragging).
  837. //
  838. bool
  839. THSplitterIndicator::CouldContain(const TPoint& point)
  840. {
  841.   TRect r = *this;
  842.   int   splitterWidth = bottom - top;
  843.  
  844.   r.left -= splitterWidth;
  845.   r.right += splitterWidth;
  846.  
  847.   TPoint p = point;
  848.   return r.Contains(point);
  849. }
  850.  
  851. //
  852. // Calculate the area in which the indicator moved.  Use original splitter
  853. // position and current indicator position to calculate area. Used to
  854. // determine which panes need to be removed.
  855. //
  856. TRect
  857. THSplitterIndicator::CalcAreaOfSplitterMove()
  858. {
  859.   TRect r1 = *this;
  860.   TRect r2 = Splitter->GetScreenRect();
  861.   TRect area = r2;
  862.  
  863.   if (r1.top < r2.top) {
  864.     area.top = r1.top;
  865.     area.bottom = r2.bottom;
  866.   }
  867.   else {
  868.     area.top = r2.top;
  869.     area.bottom = r1.bottom;
  870.   }
  871.   return area;
  872. }
  873.  
  874. //----------------------------------------------------------------------------
  875. // TSplitterIndicatorList class.
  876. //
  877.  
  878. //
  879. // Search for indicator that was created from given splitter.
  880. //
  881. TSplitterIndicator*
  882. TSplitterIndicatorList::FindIndicatorWithSplitter(TSplitter* splitter)
  883. {
  884.   TSplitterIndicatorListIterator  iter(*this);
  885.  
  886.   while (iter != 0) {
  887.     TSplitterIndicator* i = iter++;
  888.     if (i->GetSplitter() == splitter)
  889.       return i;
  890.   }
  891.   return 0;
  892. }
  893.  
  894. //----------------------------------------------------------------------------
  895. // TSplitterIndicatorMgr class.
  896. //
  897.  
  898. //
  899. // Save indicator list for later manipulation.  Record starting drag point.
  900. // Draw splitter indicators.
  901. //
  902. void
  903. TSplitterIndicatorMgr::StartMove(TSplitterIndicatorList& sil,
  904.                                  const TPoint& point)
  905. {
  906.   SplitterIndicatorList = &sil;
  907.   StartDragPoint = point;
  908.   DrawIndicators();
  909. }
  910.  
  911. //
  912. // Clear indicators from screen.
  913. //
  914. void
  915. TSplitterIndicatorMgr::EndMove()
  916. {
  917.   ClearIndicators();
  918. }
  919.  
  920. //
  921. // Move all indicators.  Distance moved is based on starting drag point and
  922. // given point.
  923. //
  924. void
  925. TSplitterIndicatorMgr::MoveIndicators(const TPoint& point)
  926. {
  927.   ClearIndicators();
  928.  
  929.   TSplitterIndicatorListIterator  iter(*SplitterIndicatorList);
  930.   if (iter) {
  931.     TSplitterIndicator* main = iter.Current();
  932.     TSplitterIndicator* si;
  933.     while (iter) {
  934.       si = iter++;
  935.       int dist = si->CalcDistMoved(StartDragPoint, point);
  936.       si->Move(dist);
  937.       if (si != main)
  938.         si->ConnectToRect(*main);
  939.     }
  940.   }
  941.   DrawIndicators();
  942. }
  943.  
  944. //
  945. // Draw indicators on screen.
  946. //
  947. void
  948. TSplitterIndicatorMgr::DrawIndicators()
  949. {
  950.   if( !SplitterIndicatorList )
  951.     return;
  952.   TSplitterIndicatorListIterator  iter(*SplitterIndicatorList);
  953.   while (iter != 0)
  954.     (iter++)->Draw();
  955. }
  956.  
  957. //
  958. // Clear all indicators from screen.
  959. //
  960. void
  961. TSplitterIndicatorMgr::ClearIndicators()
  962. {
  963.   if( !SplitterIndicatorList )
  964.     return;
  965.   TSplitterIndicatorListIterator  iter(*SplitterIndicatorList);
  966.   while (iter != 0)
  967.     (iter++)->Clear();
  968. }
  969.  
  970. //----------------------------------------------------------------------------
  971. // TPaneSplitter class
  972. //
  973.  
  974. DEFINE_RESPONSE_TABLE1(TPaneSplitter, TLayoutWindow)
  975.   EV_WM_SIZE,
  976. END_RESPONSE_TABLE;
  977.  
  978.  
  979. //
  980. // Initialize data members.
  981. //
  982. TPaneSplitter::TPaneSplitter(TWindow*        parent,
  983.                              const char far* title,
  984.                              int             splitterWidth,
  985.                              TModule*        module)
  986. :
  987.   TLayoutWindow(parent, title, module),
  988.   ShouldDelete(TShouldDelete::NoDelete),
  989.   SplitterWidth(splitterWidth),
  990.   SplitterCushion(0),
  991.   Dragging(false),
  992.   PaneSplitterResizing(false)
  993. {
  994.   if (!SplitterWidth)
  995.     SplitterWidth = TSystem::Has3dUI() ? TUIMetric::CxFixedFrame
  996.                                        : TUIMetric::CxSizeFrame;
  997.   SetCaption("PaneSplitter");
  998. }
  999.  
  1000. //
  1001. // Empty destructor
  1002. //
  1003. TPaneSplitter::~TPaneSplitter()
  1004. {
  1005. }
  1006.  
  1007. //
  1008. // Load cursors and set TSplitter's pane splitter object.
  1009. //
  1010. void
  1011. TPaneSplitter::SetupWindow()
  1012. {
  1013.   TLayoutWindow::SetupWindow();
  1014.  
  1015.   // Load cursors.
  1016.   //
  1017. #if defined(USE_CUSTOM_CURSORS)
  1018.   ResizeCursorH = GetModule()->LoadCursor(IDC_RESIZE_H);
  1019.   ResizeCursorV = GetModule()->LoadCursor(IDC_RESIZE_V);
  1020.   ResizeCursorHV = GetModule()->LoadCursor(IDC_RESIZE_HV);
  1021. #else
  1022.   ResizeCursorH = ::LoadCursor(0, IDC_SIZENS);
  1023.   ResizeCursorV = ::LoadCursor(0, IDC_SIZEWE);
  1024.   ResizeCursorHV = ::LoadCursor(0, IDC_SIZEALL);
  1025. #endif
  1026. }
  1027.  
  1028. //
  1029. // Remove all panes (and related splitters).  Destroy cursors.
  1030. //
  1031. void
  1032. TPaneSplitter::CleanupWindow()
  1033. {
  1034.   RemoveAllPanes();
  1035.  
  1036. #if defined(USE_CUSTOM_CURSORS)
  1037.   ::DestroyCursor(ResizeCursorH);
  1038.   ::DestroyCursor(ResizeCursorV);
  1039.   ::DestroyCursor(ResizeCursorHV);
  1040. #endif
  1041.  
  1042.   TLayoutWindow::CleanupWindow();
  1043. }
  1044.  
  1045. //
  1046. // Set data member 'PaneSplitterResizing' to indicate that contained
  1047. // splitters should adjust themselves according to their percentage.
  1048. //
  1049. void
  1050. TPaneSplitter::EvSize(uint sizeType, TSize& size)
  1051. {
  1052.   PaneSplitterResizing = true;
  1053.   TLayoutWindow::EvSize(sizeType, size);
  1054.   PaneSplitterResizing = false;
  1055. }
  1056.  
  1057. //
  1058. // Called by TSplitter when it needs to draw itself.
  1059. // Default behavior provided in this base class is to draw a 3dface splitter.
  1060. //
  1061. void
  1062. TPaneSplitter::DrawSplitter(TDC& dc, const TRect& splitter)
  1063. {
  1064.   dc.TextRect(splitter, TColor::Sys3dFace);
  1065.   if (!TSystem::Has3dUI())
  1066.     TUIBorder(splitter, TUIBorder::WndRaised).Paint(dc);  
  1067. }
  1068.  
  1069. //
  1070. // Split given pane, 'target', with 'newPane' in either the vertical or
  1071. // horizontal direction.  Creates a new splitter.
  1072. //
  1073. bool
  1074. TPaneSplitter::SplitPane(TWindow* target, TWindow* newPane,
  1075.                          TSplitDirection splitDir, float percent)
  1076. {
  1077.   PRECONDITION(target);
  1078.   PRECONDITION(target != newPane);
  1079.  
  1080.   if (GetFirstChild() == 0) {     // if no panes.
  1081.     TLayoutMetrics  lmOfTarget;
  1082.  
  1083.     GetDefLM(lmOfTarget);
  1084.     target->SetParent(this);
  1085.     target->Create();
  1086.     SetChildLayoutMetrics(*target, lmOfTarget);
  1087.     Layout();
  1088.   }
  1089.  
  1090.   if (newPane) {
  1091.     if (!HasPane(target))
  1092.       return false;
  1093.  
  1094.     TSplitter*      splitter;
  1095.     TLayoutWindow*  parentSplitter = LAYOUTWINDOW(target->Parent);
  1096.  
  1097.     if (parentSplitter == this) {   // if first split.
  1098.       if (splitDir == psHorizontal)
  1099.         splitter = new THSplitter(target->Parent, this, percent);
  1100.       else
  1101.         splitter = new TVSplitter(target->Parent, this, percent);
  1102.       splitter->Create();
  1103.       splitter->SetPercent(percent);
  1104.  
  1105.       TLayoutMetrics lm = splitter->Setup(target, newPane, percent);
  1106.       parentSplitter->SetChildLayoutMetrics(*splitter, lm);
  1107.     }
  1108.     else {
  1109.       // Ask splitter to split itself.
  1110.       //
  1111.       SPLITTER(parentSplitter)->Split(target, newPane, splitDir, percent);
  1112.     }
  1113.     parentSplitter->Layout();
  1114.   }
  1115.   return true;
  1116. }
  1117.  
  1118. //
  1119. // Remove given pane from TPaneSplitter, deleting it if requested.
  1120. // This operation has the side effect of removing the splitter it was
  1121. // contained in.
  1122. //
  1123. bool
  1124. TPaneSplitter::RemovePane(TWindow* pane, TPaneSplitter::TDelete dt)
  1125. {
  1126.   TLayoutWindow* layoutWin = DoRemovePane(pane, dt);
  1127.   if (layoutWin) {
  1128.     PaneSplitterResizing = true;
  1129.     layoutWin->Layout();
  1130.     PaneSplitterResizing = false;
  1131.     return true;
  1132.   }
  1133.   return false;
  1134. }
  1135.  
  1136. //
  1137. // Replace 'target' pane (must exist) with 'newPane' (does not exist).
  1138. // 'target' may be deleted, depends on 'dt'.
  1139. //
  1140. bool
  1141. TPaneSplitter::ReplacePane(TWindow* target, TWindow* newPane,
  1142.                            TPaneSplitter::TDelete dt)
  1143. {
  1144.   if (!HasPane(target) || HasPane(newPane)) // 'newPane' should not exist.
  1145.     return false;
  1146.  
  1147.   if (target->CanClose()) {     // 'newPane' takes on target's layout metrics.
  1148.     TLayoutMetrics  lmOfTarget;
  1149.     TLayoutMetrics  lmOfOther;
  1150.     TLayoutWindow*  splitter = LAYOUTWINDOW(target->Parent);
  1151.     TWindow*        pane1 = splitter->GetFirstChild();
  1152.     TWindow*        pane2 = 0;
  1153.  
  1154.     if (SPLITTER(splitter)) {
  1155.       pane1 = SPLITTER(splitter)->Pane1();
  1156.       pane2 = SPLITTER(splitter)->Pane2();
  1157.     }
  1158.     splitter->GetChildLayoutMetrics(*target, lmOfTarget);
  1159.  
  1160.     if (pane1 == target && pane2) {
  1161.       splitter->GetChildLayoutMetrics(*pane2, lmOfOther);
  1162.       if (lmOfOther.X.RelWin == target)
  1163.         lmOfOther.X.RelWin = newPane;
  1164.       else
  1165.         lmOfOther.Y.RelWin = newPane;
  1166.       splitter->SetChildLayoutMetrics(*pane2, lmOfOther);
  1167.     }
  1168.     newPane->SetParent(splitter);
  1169.     newPane->Create();
  1170.     splitter->SetChildLayoutMetrics(*newPane, lmOfTarget);
  1171.     splitter->RemoveChildLayoutMetrics(*target);
  1172.     DestroyPane(target, dt);
  1173.     splitter->Layout();
  1174.     return true;
  1175.   }
  1176.   return false;
  1177. }
  1178.  
  1179. //
  1180. // Swap given panes (must exist).  Panes take on each others layout metrics.
  1181. //
  1182. bool
  1183. TPaneSplitter::SwapPanes(TWindow* pane1, TWindow* pane2)
  1184. {
  1185.   if (!HasPane(pane1) || !HasPane(pane2))
  1186.     return false;
  1187.  
  1188.   TSplitter* splitter1 = SPLITTER(pane1->Parent);
  1189.   TSplitter* splitter2 = SPLITTER(pane2->Parent);
  1190.  
  1191.   if (splitter1 != splitter2) {
  1192.     TLayoutMetrics  lmOfPane1;
  1193.     TLayoutMetrics  lmOfPane2;
  1194.     TLayoutMetrics  lmOfOther;
  1195.     TWindow*        paneA = splitter1->Pane1();
  1196.     TWindow*        paneB = splitter1->Pane2();
  1197.  
  1198.     splitter1->GetChildLayoutMetrics(*pane1, lmOfPane1);
  1199.     splitter2->GetChildLayoutMetrics(*pane2, lmOfPane2);
  1200.  
  1201.     if (paneA == pane1) {   // if top or left pane.
  1202.       splitter1->GetChildLayoutMetrics(*paneB, lmOfOther);
  1203.       pane1->SetParent(0);
  1204.       if (lmOfOther.X.RelWin == pane1)
  1205.         lmOfOther.X.RelWin = pane2;
  1206.       else
  1207.         lmOfOther.Y.RelWin = pane2;
  1208.       splitter1->SetChildLayoutMetrics(*paneB, lmOfOther);
  1209.     }
  1210.  
  1211.     paneA = splitter2->Pane1();
  1212.     paneB = splitter2->Pane2();
  1213.     if (paneA == pane2) {   // if top or left pane.
  1214.       splitter2->GetChildLayoutMetrics(*paneB, lmOfOther);
  1215.       pane2->SetParent(0);
  1216.       if (lmOfOther.X.RelWin == pane2)
  1217.         lmOfOther.X.RelWin = pane1;
  1218.       else
  1219.         lmOfOther.Y.RelWin = pane1;
  1220.       splitter2->SetChildLayoutMetrics(*paneB, lmOfOther);
  1221.     }
  1222.     pane1->SetParent(splitter2);
  1223.     pane2->SetParent(splitter1);
  1224.     splitter1->SetChildLayoutMetrics(*pane2, lmOfPane1);
  1225.     splitter2->SetChildLayoutMetrics(*pane1, lmOfPane2);
  1226.     splitter1->Layout();
  1227.     splitter2->Layout();
  1228.   }
  1229.   else {
  1230.     TLayoutWindow*  lw = LAYOUTWINDOW(pane1->Parent);
  1231.     TLayoutMetrics  lmOfPane1;
  1232.     TLayoutMetrics  lmOfPane2;
  1233.  
  1234.     lw->GetChildLayoutMetrics(*pane1, lmOfPane1);
  1235.     lw->GetChildLayoutMetrics(*pane2, lmOfPane2);
  1236.  
  1237.     if (lmOfPane1.X.RelWin == pane2)
  1238.       lmOfPane1.X.RelWin = pane1;
  1239.     else if (lmOfPane2.X.RelWin == pane1)
  1240.       lmOfPane2.X.RelWin = pane2;
  1241.     else if (lmOfPane1.Y.RelWin == pane2)
  1242.       lmOfPane1.Y.RelWin = pane1;
  1243.     else
  1244.       lmOfPane2.Y.RelWin = pane2;
  1245.  
  1246.     lw->SetChildLayoutMetrics(*pane1, lmOfPane2);
  1247.     lw->SetChildLayoutMetrics(*pane2, lmOfPane1);
  1248.     lw->Layout();
  1249.   }
  1250.   return true;
  1251. }
  1252.  
  1253. //
  1254. // Iterate over each pane.  if 'callback' returns 0 then iteration stops.
  1255. //
  1256. void
  1257. TPaneSplitter::ForEachPane(TPaneSplitter::TForEachPaneCallback callback,
  1258.                            void* p)
  1259. {
  1260.   ForEachObject(GetFirstChild(), &TPaneSplitter::DoForEachPane,
  1261.                 (void*)callback, p);  
  1262. }
  1263.  
  1264. //
  1265. // Return number of panes in TPaneSplitter.
  1266. //
  1267. int
  1268. TPaneSplitter::PaneCount()
  1269. {
  1270.   int nPanes = 0;
  1271.  
  1272.   ForEachObject(GetFirstChild(), &TPaneSplitter::DoPaneCount, &nPanes, 0);
  1273.   return nPanes;
  1274. }
  1275.  
  1276. //
  1277. // Remove all the panes (and their splitters).  If any pane can't close
  1278. // then abort the operation.
  1279. //
  1280. void
  1281. TPaneSplitter::RemoveAllPanes(TDelete dt)
  1282. {
  1283.   TListImp<TWindow*>          wList;
  1284.  
  1285.   // Remove and delete (depending on 'dt') all panes.
  1286.   //
  1287.   ForEachObject(GetFirstChild(), &TPaneSplitter::GetPanes, &wList, 0);
  1288.   TListIteratorImp<TWindow*>  wListIter(wList);
  1289.  
  1290.   // check to see if a pane can't close, if so abort operation.
  1291.   //
  1292.   while (wListIter != 0)
  1293.     if (!(wListIter++)->CanClose())
  1294.       return;
  1295.   wListIter.Restart();
  1296.   while (wListIter != 0)    // remove all the panes.
  1297.     RemovePane(wListIter++, dt);
  1298. }
  1299.  
  1300. //
  1301. // Set the splitter width and adjust all splitters, takes effect immediately.
  1302. //
  1303. int
  1304. TPaneSplitter::SetSplitterWidth(int newWidth)
  1305. {
  1306.   int oldWidth = SplitterWidth;
  1307.   int widthDiff = newWidth - SplitterWidth;
  1308.  
  1309.   SplitterWidth = newWidth;
  1310.   ForEachObject(GetFirstChild(), &TPaneSplitter::AdjSplitterWidth, &widthDiff,
  1311.                 0);
  1312.   return oldWidth;
  1313. }
  1314.  
  1315. //
  1316. // Move 'pane' given distance.  if 'pane' does not exist or there are
  1317. // no splitters or removal of a pane failed then false is returned, else true.
  1318. //
  1319. bool
  1320. TPaneSplitter::MoveSplitter(TWindow* pane, int dist)
  1321. {
  1322.   if (!HasPane(pane) || pane->Parent == this )
  1323.     return false;
  1324.  
  1325.   TSplitter*    splitter = SPLITTER(pane->Parent);
  1326.   TSplitterIndicator* si = splitter->CreateSplitterIndicator();
  1327.   si->SetCushion( SplitterCushion );
  1328.  
  1329.   SplitterIndicatorList.Flush(1);
  1330.   SplitterIndicatorList.Add(si);
  1331.   si->Move(dist);
  1332.   if (RemovePanes() == 0) {
  1333.     MoveSplitters();
  1334.     SplitterIndicatorList.Flush(1);
  1335.     return true;
  1336.   }
  1337.   return false;
  1338. }
  1339.  
  1340. //
  1341. // Private Member Functions...
  1342. //
  1343.  
  1344. //
  1345. // Notify indicator mgr to move the indicators.
  1346. //
  1347. void
  1348. TPaneSplitter::MouseMoved(const TPoint& point)
  1349. {
  1350.   if (Dragging)
  1351.     SplitterIndicatorMgr.MoveIndicators(point);
  1352. }
  1353.  
  1354. //
  1355. // Set capture (for mouse dragging) and notify indicator mgr that
  1356. // we are starting to move the indicators.
  1357. //
  1358. void
  1359. TPaneSplitter::StartSplitterMove(TSplitter* splitter, const TPoint& point)
  1360. {
  1361.   Dragging = true;
  1362.   splitter->SetCapture();
  1363.   SplitterIndicatorMgr.StartMove(SplitterIndicatorList, point);
  1364. }
  1365.  
  1366. //
  1367. // Release the capture and notify indicator mgr that we have stopped moving
  1368. // the indicators.  Remove any panes that were 'covered' by the move.
  1369. // Finally, move the splitters.
  1370. //
  1371. void
  1372. TPaneSplitter::EndSplitterMove()
  1373. {
  1374.   if( !Dragging )
  1375.     return;
  1376.   Dragging = false;
  1377.   ReleaseCapture();
  1378.   SplitterIndicatorMgr.EndMove();
  1379.   if (RemovePanes() == 0) {
  1380.     MoveSplitters();
  1381.     SplitterIndicatorList.Flush(1);
  1382.   }
  1383. }
  1384.  
  1385. //
  1386. // Set the appropriate cursor depending on the number of splitters
  1387. // that will be moved.
  1388. //
  1389. void
  1390. TPaneSplitter::SetSplitterMoveCursor(TSplitter* splitter, const TPoint& point)
  1391. {
  1392.   if (!Dragging)
  1393.     FindIntersectingSplitters(point);
  1394.  
  1395.   if (SplitterIndicatorList.NSplitterIndicators() > 1)
  1396.     ::SetCursor(ResizeCursorHV);
  1397.   else if (splitter->SplitDirection() == psHorizontal)
  1398.     ::SetCursor(ResizeCursorH);
  1399.   else
  1400.     ::SetCursor(ResizeCursorV);
  1401. }
  1402.  
  1403. //
  1404. // Private structure for splitter and pane traversal.
  1405. //
  1406. struct TTraversalRec {
  1407.   TTraversalRec() : Cnt(0), Object(0) {}
  1408.   TTraversalRec(TWindow* o) : Cnt(0), Object(o) {}
  1409.   TTraversalRec(const TTraversalRec& r) : Cnt(r.Cnt), Object(r.Object) {}
  1410.  
  1411.   int operator == (const TTraversalRec& r) const { return Object == r.Object; }
  1412.   TTraversalRec& operator = (const TTraversalRec& r);
  1413.  
  1414.   char              Cnt;
  1415.   TWindow*          Object;
  1416. };
  1417.  
  1418. TTraversalRec&
  1419. TTraversalRec::operator = (const TTraversalRec& r)
  1420. {
  1421.   Cnt = r.Cnt; Object = r.Object;
  1422.   return *this;
  1423. }
  1424.  
  1425. //
  1426. // Iterate over the splitters and panes, calling 'callback' on each.
  1427. // allows for 2 parameters to be passed.  Traverses the objects in
  1428. // one of three order: InOrder, PreOrder and PostOrder.
  1429. //
  1430. void
  1431. TPaneSplitter::ForEachObject(TWindow* o, TForEachObjectCallback callback,
  1432.                              void* p1, void* p2, TTraversalOrder order)
  1433. {
  1434.   if (!o)
  1435.     return;
  1436.  
  1437.   TStackAsList<TTraversalRec>     stack;
  1438.   TTraversalRec                   trec;
  1439.   int                             done = 0;
  1440.  
  1441.   stack.Push(TTraversalRec(o));
  1442.   while (!done) {
  1443.     trec = stack.Pop();
  1444.     trec.Cnt++;
  1445.     if ((order == psPreOrder && trec.Cnt == 1) ||
  1446.         (order == psInOrder && trec.Cnt == 2)  ||
  1447.         (order == psPostOrder && trec.Cnt == 3))
  1448.       done = !(this->*callback)(trec.Object, p1, p2);
  1449.  
  1450.     if (!done) {
  1451.       if (trec.Cnt != 3) {
  1452.         stack.Push(trec);
  1453.         if (trec.Cnt == 1 && SPLITTER(trec.Object))
  1454.           stack.Push(TTraversalRec(SPLITTER(trec.Object)->Pane1()));
  1455.         else if (trec.Cnt == 2 && SPLITTER(trec.Object) &&
  1456.                  SPLITTER(trec.Object)->Pane2())
  1457.           stack.Push(TTraversalRec(SPLITTER(trec.Object)->Pane2()));
  1458.       }
  1459.       done = stack.IsEmpty();
  1460.     }
  1461.   }
  1462. }
  1463.  
  1464. //
  1465. // Set default TLayoutMetrics.  Everything relative to parent.
  1466. //
  1467. void
  1468. TPaneSplitter::GetDefLM(TLayoutMetrics& lm)
  1469. {
  1470.   lm.SetMeasurementUnits(lmPixels);
  1471.   lm.X.SameAs(lmParent, lmLeft);
  1472.   lm.Y.SameAs(lmParent, lmTop);
  1473.   lm.Width.Set(lmRight, lmSameAs, lmParent, lmRight);
  1474.   lm.Height.Set(lmBottom, lmSameAs, lmParent, lmBottom);
  1475. }
  1476.  
  1477. //
  1478. // Return true if this TPaneSplitter contains 'p' (pane or splitter).
  1479. //
  1480. bool
  1481. TPaneSplitter::HasPane(TWindow* p)
  1482. {
  1483.   TWindow* parent = p->Parent;
  1484.   while (parent) {
  1485.     if (parent == this)
  1486.       return true;
  1487.     parent = parent->Parent;
  1488.   }
  1489.   return false;
  1490. }
  1491.  
  1492. //
  1493. // Do the work of removing given pane.
  1494. //
  1495. TLayoutWindow*
  1496. TPaneSplitter::DoRemovePane(TWindow* pane, TPaneSplitter::TDelete dt)
  1497. {
  1498.   if (!HasPane(pane) || !pane->CanClose())
  1499.     return 0;
  1500.  
  1501.   TSplitter*        splitter = SPLITTER(pane->Parent);
  1502.   TLayoutWindow*    retval = this;
  1503.  
  1504.   if (splitter) {
  1505.     retval = splitter->RemovePane(pane, dt);
  1506.     splitter->Destroy();
  1507.     delete splitter;
  1508.   }
  1509.   else {
  1510.     RemoveChildLayoutMetrics(*pane);
  1511.     DestroyPane(pane, dt);
  1512.   }
  1513.   return retval;
  1514. }
  1515.  
  1516. //
  1517. // Find intersecting splitters.  Called when splitters will be moved.
  1518. //
  1519. void
  1520. TPaneSplitter::FindIntersectingSplitters(const TPoint& point)
  1521. {
  1522.   TPoint p = point;
  1523.  
  1524.   SplitterIndicatorList.Flush(1);
  1525.   ForEachObject(GetFirstChild(), &TPaneSplitter::DoFindIntersectingSplitters,
  1526.                 &SplitterIndicatorList, &p);
  1527. }
  1528.  
  1529. //
  1530. // Remove any panes 'covered' as a result of a splitter move.
  1531. //
  1532. int
  1533. TPaneSplitter::RemovePanes()
  1534. {
  1535.   TListImp<TSplitterIndicator*>   tempList;   // don't process indicators.
  1536.   TSplitterIndicatorListIterator  iter(SplitterIndicatorList);
  1537.   TSplitterIndicator*             si;
  1538.   int                             nPanesNotRemoved = 0;
  1539.  
  1540.   while (iter != 0) {
  1541.     si = iter++;
  1542.  
  1543.     if (!tempList.Find(si)) {   // if indicator not in list then process it.
  1544.       TRect               area = si->CalcAreaOfSplitterMove();
  1545.       TListImp<TWindow*>  list; // list of panes to remove.
  1546.       ForEachObject(GetFirstChild(), &TPaneSplitter::GetListOfPanesToRemove,
  1547.                     &list, &area);
  1548.       TListIteratorImp<TWindow*>  listIter(list);
  1549.       while (listIter != 0) {
  1550.         TWindow* pane = listIter++;
  1551.         TSplitterIndicator* i;
  1552.         if ((i = SplitterIndicatorList.FindIndicatorWithSplitter(
  1553.               SPLITTER(pane->Parent))) != 0)
  1554.           tempList.Add(i);
  1555.         if (!RemovePane(pane)) {
  1556.           tempList.Add(i);
  1557.           nPanesNotRemoved++;
  1558.           break;
  1559.         }
  1560.       }
  1561.     }
  1562.   }
  1563.   TListIteratorImp<TSplitterIndicator*>   tempIter(tempList);
  1564.   while (tempIter != 0)   // remove panes not to be moved from list.
  1565.     SplitterIndicatorList.Detach(tempIter++, 1);
  1566.   return nPanesNotRemoved;
  1567. }
  1568.  
  1569. //
  1570. // Move all splitters.
  1571. //
  1572. void
  1573. TPaneSplitter::MoveSplitters()
  1574. {
  1575.   TSplitterIndicatorListIterator  iter(SplitterIndicatorList);
  1576.   while (iter != 0) {
  1577.     TSplitterIndicator* si = iter++;
  1578.     si->GetSplitter()->Move(si->GetDistMoved());
  1579.     si->GetSplitter()->Layout();
  1580.   }
  1581. }
  1582.  
  1583. //
  1584. // Destroy a pane, deleting the OWL object if requested
  1585. //
  1586. void
  1587. TPaneSplitter::DestroyPane(TWindow* pane, TDelete dt)
  1588. {
  1589.   pane->ShowWindow(SW_HIDE);    // so user can't see.
  1590.   pane->SetParent(0);
  1591.   pane->Destroy();
  1592.   if (DelObj(dt))
  1593.     delete pane;
  1594. }
  1595.  
  1596. //
  1597. // ForEachObject callbacks...
  1598. //
  1599.  
  1600. //
  1601. // Iterate over all panes.
  1602. //
  1603. int
  1604. TPaneSplitter::DoForEachPane(TWindow* o, void *p1, void*p2)
  1605. {
  1606.   if (!SPLITTER(o))
  1607.     return ((TForEachPaneCallback)p1)(*o, p2);
  1608.   return 1;
  1609. }
  1610.  
  1611. //
  1612. // Find intersecting splitters. Determine which splitters to move.
  1613. //
  1614. int
  1615. TPaneSplitter::DoFindIntersectingSplitters(TWindow* o, void *p1, void*p2)
  1616. {
  1617.   TSplitter* splitter = SPLITTER(o);
  1618.   if (splitter) {
  1619.     TSplitterIndicator* i = splitter->CreateSplitterIndicator();
  1620.     i->SetCushion( SplitterCushion );
  1621.     if (i->CouldContain(*(TPoint*)p2))
  1622.       ((TSplitterIndicatorList*)p1)->Add(i);
  1623.     else
  1624.       delete i;
  1625.   }
  1626.   return 1;
  1627. }
  1628.  
  1629. //
  1630. // Get a list of panes that will be removed as a result of a splitter move.
  1631. //
  1632. int
  1633. TPaneSplitter::GetListOfPanesToRemove(TWindow* o, void *p1, void*p2)
  1634. {
  1635.   if (!SPLITTER(o)) {
  1636.     TRect r = o->GetWindowRect();
  1637.     if ((*(TRect*)p2).Contains(r))
  1638.       ((TListImp<TWindow*>*)p1)->Add(o);
  1639.   }
  1640.   return 1;
  1641. }
  1642.  
  1643. //
  1644. // Change the width of every splitter.
  1645. //
  1646. int
  1647. TPaneSplitter::AdjSplitterWidth(TWindow* o, void *p1, void*)
  1648. {
  1649.   TSplitter* splitter = SPLITTER(o);
  1650.  
  1651.   if (splitter)
  1652.     splitter->AdjSplitterWidth(*(int *)p1);
  1653.   return 1;
  1654. }
  1655.  
  1656. //
  1657. // Calculate number of panes.
  1658. //
  1659. int
  1660. TPaneSplitter::DoPaneCount(TWindow* o, void *p1, void*)
  1661. {
  1662.   if (!SPLITTER(o))
  1663.     (*(int*)p1)++;
  1664.   return 1;
  1665. }
  1666.  
  1667. //
  1668. // Get a list of all the panes.
  1669. //
  1670. int
  1671. TPaneSplitter::GetPanes(TWindow* o, void *p1, void*)
  1672. {
  1673.   if (!SPLITTER(o))
  1674.     ((TListImp<TWindow*> *)p1)->Add(o);
  1675.   return 1;
  1676. }
  1677.  
  1678. //
  1679. // Get a list of all splitters.
  1680. //
  1681. int
  1682. TPaneSplitter::GetSplitters(TWindow* o, void* p1, void*)
  1683. {
  1684.   if (SPLITTER(o))
  1685.     ((TListImp<TWindow*>*)p1)->Add(o);
  1686.   return 1;
  1687. }
  1688.